package com.whut.util.concurrent.locks;

import lombok.extern.slf4j.Slf4j;

import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;

/**
 * @author wangzonghe
 * @date 2025/8/8 13:07
 */
@Slf4j
public class MyReentrantLock extends BaseLock {

    private final AtomicReference<MyBlockingQueue> head = new AtomicReference<>(new MyBlockingQueue());

    private final AtomicReference<MyBlockingQueue> tail = new AtomicReference<>(head.get());

    private Thread owner;

    private final AtomicLong status = new AtomicLong(0);

    private static final String LOCK_SUCCESS_MSG = "线程{}获取锁成功";

    @Override
    public void lockUnfair() {
        // 原子判断status是否为0，为0设置为1，表示加锁；加锁成功直接返回
        if (status.compareAndSet(0, 1)) {
            owner = Thread.currentThread();
            log.info(LOCK_SUCCESS_MSG, Thread.currentThread().getName());
            return;
        }
        lockFair();
    }

    @Override
    public void lockFair() {
        // 如果当前线程id和head的线程id相同，则表示当前线程已经加锁，增加status；加锁成功，直接返回
        if (Thread.currentThread().equals(owner)) {
            long count = status.incrementAndGet();
            log.info("线程{}获取锁成功，重入次数{}", Thread.currentThread().getName(), count);
            return;
        }
        MyBlockingQueue current = new MyBlockingQueue();
        // 创建一个节点，并设置当前线程id，然后将当前节点通过尾插法插入
        addTail(current);

        wait(current);
    }

    @Override
    public void unlock() {
        if (!Thread.currentThread().equals(owner)) {
            throw new IllegalMonitorStateException("当前线程不是锁的拥有者，无法解锁");
        }
        long count = status.get();
        if (count <= 0) {
            throw new IllegalMonitorStateException("当前锁未被持有，无法解锁");
        }

        if (count > 1) {
            // 只会有一个线程进入unlock，所以直接将count减1即可
            status.set(count - 1);
            return;
        }
        owner = null;
        status.set(0);
        MyBlockingQueue next = head.get().next;
        if (Objects.nonNull(next) && Objects.nonNull(next.thread)) {
            LockSupport.unpark(next.thread);
        }
    }

    @Override
    public boolean tryLock() {
        // 原子判断status是否为0，为0设置为1，表示加锁；加锁成功直接返回true
        if (status.compareAndSet(0, 1)) {
            owner = Thread.currentThread();
            log.info(LOCK_SUCCESS_MSG, Thread.currentThread().getName());
            return true;
        }
        // 如果当前线程id和head的线程id相同，则表示当前线程已经加锁，增加status；加锁成功，直接返回true
        if (Thread.currentThread().equals(owner)) {
            status.incrementAndGet();
            log.info("线程{}获取锁成功，重入次数{}", Thread.currentThread().getName(), status.get());
            return true;
        }
        return false;
    }

    @Override
    public void unReentrantLockUnFair() {
        MyBlockingQueue current = new MyBlockingQueue();
        // 创建一个节点，并设置当前线程id，然后将当前节点通过尾插法插入
        addTail(current);

        wait(current);
    }

    @Override
    public void unReentrantLockFair() {
        // 原子判断status是否为0，为0设置为1，表示加锁；加锁成功直接返回
        if (status.compareAndSet(0, 1)) {
            owner = Thread.currentThread();
            log.info(LOCK_SUCCESS_MSG, Thread.currentThread().getName());
            return;
        }
        unReentrantLockUnFair();
    }


    private void wait(MyBlockingQueue current) {
        while (true) {
            // 如果当前节点的前一个节点是head，并且status为0，则表示当前线程可以获取锁，设置status为1，并返回
            if (head.get().equals(current.pre) && status.compareAndSet(0, 1)) {
                owner = Thread.currentThread();
                head.get().next = null;
                current.pre = null;
                head.set(current);
                log.info(LOCK_SUCCESS_MSG, Thread.currentThread().getName());
                break;
            }
            LockSupport.park();
        }
    }


    /**
     * 创建一个节点，并设置当前线程id，然后将当前节点通过尾插法插入
     */
    private void addTail(MyBlockingQueue current) {
        current.thread = Thread.currentThread();

        // 由于tail在进行修改时，可能会被其他线程修改掉，所以需要循环获取currentTail，循环重试，直到成功
        while (true) {
            MyBlockingQueue currentTail = tail.get();
            if (tail.compareAndSet(currentTail, current)) {
                currentTail.next = current;
                current.pre = currentTail;
                log.info("线程{}插入阻塞队列", Thread.currentThread().getName());
                break;
            }
        }
    }

    private static class MyBlockingQueue {

        /**
         * 线程引用
         */
        private Thread thread;

        /**
         * 上一个节点
         */
        private MyBlockingQueue pre;

        /**
         * 下一个节点
         */
        private MyBlockingQueue next;
    }
}
